package cn.iocoder.an.module.mold.job;

import cn.iocoder.an.framework.common.util.business.CreateKeepNo;
import cn.iocoder.an.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.an.framework.quartz.core.handler.JobHandler;
import cn.iocoder.an.framework.tenant.core.job.TenantJob;
import cn.iocoder.an.module.mold.dal.dataobject.equipmentkeepconfig.EquipmentKeepConfigDO;
import cn.iocoder.an.module.mold.dal.dataobject.equipmentkeepsettingtype.EquipmentKeepSettingTypeDO;
import cn.iocoder.an.module.business.dal.dataobject.equipmentmanage.EquipmentManageDO;
import cn.iocoder.an.module.mold.dal.dataobject.equipmnetkeepwait.EquipmnetKeepWaitDO;
import cn.iocoder.an.module.mold.dal.mysql.equipmentkeepconfig.EquipmentKeepConfigMapper;
import cn.iocoder.an.module.mold.dal.mysql.equipmentkeepsettingtype.EquipmentKeepSettingTypeMapper;
import cn.iocoder.an.module.business.dal.mysql.equipmentmanage.EquipmentManageMapper;
import cn.iocoder.an.module.mold.dal.mysql.equipmnetkeepwait.EquipmnetKeepWaitMapper;
import cn.iocoder.an.module.mold.dal.mysql.keeptask.KeepTaskMapper;
import cn.iocoder.an.module.business.dal.mysql.warningconfig.WarningConfigMapper;
import cn.iocoder.an.module.business.enums.KeepStatusEnum;
import cn.iocoder.an.module.mold.service.equipmentkeepsettingtype.EquipmentKeepSettingTypeService;
import cn.iocoder.an.module.business.utils.WeComMessageUtil;
import cn.iocoder.an.module.system.api.notify.NotifyMessageSendApi;
import cn.iocoder.an.module.system.service.dict.DictDataService;
import cn.iocoder.an.module.system.service.user.AdminUserService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import jakarta.annotation.Resource;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Component
public class CreateEquipmentDamageJob implements JobHandler {

    @Resource
    private WarningConfigMapper warningConfigMapper;

    @Autowired
    private EquipmnetKeepWaitMapper equipmnetKeepWaitMapper;

    @Resource
    private AdminUserService userService;

    @Autowired
    private WeComMessageUtil weComMessageUtil;

    @Resource
    private NotifyMessageSendApi notifySendApi;

    @Resource
    private DictDataService dictDataService;

    @Resource
    private EquipmentKeepSettingTypeMapper equipmentKeepSettingTypeMapper;

    @Resource
    private EquipmentKeepConfigMapper equipmentKeepConfigMapper;

    @Resource
    private EquipmentManageMapper equipmentManageMapper;

    @Autowired
    private RedissonClient redissonClient;

    @Resource
    private EquipmentKeepSettingTypeService equipmentKeepSettingTypeService;

    // 可放在 EquipmnetKeepWaitDO 类中，或一个状态常量类中
    public static final Set<Integer> EXCLUDED_STATUS_SET = Set.of(5, 10, 15, 20, 23);
    @Autowired
    private KeepTaskMapper keepTaskMapper;


    @Override
    @TenantJob // 标记多租户
    public String execute(String param) throws Exception {

        LambdaQueryWrapperX<EquipmentManageDO> equipmentManageWrapper = new LambdaQueryWrapperX<>();
        equipmentManageWrapper.isNotNull(EquipmentManageDO::getLastMaintainTimeDamage);
        List<EquipmentManageDO> equipmentManageDOS = equipmentManageMapper.selectList(equipmentManageWrapper);
        for (EquipmentManageDO equipmentManageDO : equipmentManageDOS) {
            if (equipmentManageDO.getLastMaintainTimeDamage() == null) {
                continue;
            }
            LambdaQueryWrapper<EquipmentKeepConfigDO> configWrapper = new LambdaQueryWrapper<>();
            configWrapper.eq(EquipmentKeepConfigDO::getConfigType, 2); // 1按分类 2按设备
            configWrapper.eq(EquipmentKeepConfigDO::getTargetId, equipmentManageDO.getId());
            // 指定只查询 itemId 字段
            configWrapper.select(EquipmentKeepConfigDO::getItemId);
            List<EquipmentKeepConfigDO> equipmentKeepConfigDOS = equipmentKeepConfigMapper.selectList(configWrapper);
            Integer type = 2;
            if (CollectionUtils.isEmpty(equipmentKeepConfigDOS)) {
                //按设备没有查询到配置  按分类再查询一遍
                LambdaQueryWrapper<EquipmentKeepConfigDO> configWrapper2 = new LambdaQueryWrapper<>();
                configWrapper2.eq(EquipmentKeepConfigDO::getConfigType, 1); // 1按分类 2按设备
                configWrapper2.eq(EquipmentKeepConfigDO::getTargetId, equipmentManageDO.getEquipmentClassify());
                // 指定只查询 itemId 字段
                configWrapper.select(EquipmentKeepConfigDO::getItemId);
                equipmentKeepConfigDOS = equipmentKeepConfigMapper.selectList(configWrapper2);
                type = 1;
            }


            for (EquipmentKeepConfigDO equipmentKeepConfigDO : equipmentKeepConfigDOS) {


                //1.查询所有已配置过的 启用状态的
                LambdaQueryWrapperX<EquipmentKeepSettingTypeDO> settingTypeWrapper = new LambdaQueryWrapperX<>();
                settingTypeWrapper.eq(EquipmentKeepSettingTypeDO::getStatus, 1);
                settingTypeWrapper.isNotNull(EquipmentKeepSettingTypeDO::getKeepSetting);
                EquipmentKeepSettingTypeDO equipmentKeepSettingTypeDO = equipmentKeepSettingTypeMapper.selectFirstOne(EquipmentKeepSettingTypeDO::getId, equipmentKeepConfigDO.getItemId());

                if (equipmentKeepSettingTypeDO == null) {
                    continue;
                }

                //周期
                Integer cycle = equipmentKeepSettingTypeDO.getCycle();

                if (cycle == null) {
                    continue;
                }

                Long itemId = equipmentKeepConfigDO.getItemId();


                //直接生成保养任务
                if (type == 1) {//按设备
                    createWaitKeepEquipmentData(1, equipmentManageDO.getEquipmentClassify(), itemId, type);
                } else if (type == 2) {
                    createWaitKeepEquipmentData(2, equipmentManageDO.getId(), itemId, type);
                }

            }
        }

        //删除重复待保养
// 查询所有需要处理的状态下的记录
        LambdaQueryWrapper<EquipmnetKeepWaitDO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.in(EquipmnetKeepWaitDO::getStatus, EXCLUDED_STATUS_SET);
        queryWrapper.orderByDesc(EquipmnetKeepWaitDO::getUpdateTime);

        List<EquipmnetKeepWaitDO> allList = equipmnetKeepWaitMapper.selectList(queryWrapper);

// 按 equipmentId 分组，每组只保留 update_time 最大的那个 id
        Set<Long> idsToKeep = allList.stream()
                .collect(Collectors.groupingBy(
                        EquipmnetKeepWaitDO::getEquipmentId,
                        Collectors.collectingAndThen(
                                Collectors.maxBy(Comparator.comparing(EquipmnetKeepWaitDO::getUpdateTime)),
                                opt -> opt.map(EquipmnetKeepWaitDO::getId).orElse(null)
                        )
                ))
                .values()
                .stream()
                .filter(Objects::nonNull)
                .collect(Collectors.toSet());

// 删除不在保留列表中的记录
        if (!idsToKeep.isEmpty()) {
            LambdaQueryWrapper<EquipmnetKeepWaitDO> deleteWrapper = new LambdaQueryWrapper<>();
            deleteWrapper.notIn(EquipmnetKeepWaitDO::getId, idsToKeep);
            deleteWrapper.in(EquipmnetKeepWaitDO::getStatus, EXCLUDED_STATUS_SET);
            equipmnetKeepWaitMapper.delete(deleteWrapper);
        }



        return "success";
    }


    @Transactional(rollbackFor = Exception.class)
    public void createWaitKeepEquipmentData(Integer currentType, Long targetId, Long itemId, Integer type) {
        if (currentType == null || targetId == null || itemId == null) {
            return;
        }

        // 当前年月，用于单号前缀
        String yearMonth = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMM"));
        String prefix = CreateKeepNo.PREFIXWH; // "WH"
        String prefixPattern = prefix + yearMonth; // "BY202508"

        // 🔐 分布式锁 key：按月隔离，不同月不互斥
        String lockKey = "lock:keep_no:" + prefixPattern;
        RLock lock = redissonClient.getLock(lockKey);

        try {
            // 获取锁（最多等待10秒，持有30秒自动释放）
            boolean isLocked = lock.tryLock(20, 60, TimeUnit.SECONDS);
            if (!isLocked) {
                throw new RuntimeException("获取分布式锁失败，无法生成唯一单号");
            }

            // ✅ 在锁内查询当前最大单号
            String maxKeepNo = equipmnetKeepWaitMapper.findMaxKeepNo(prefixPattern);

            // ✅ 生成下一个基础单号（如 BY2025080001）
            String nextBaseNo = CreateKeepNo.generateNextCode(maxKeepNo);

            List<EquipmnetKeepWaitDO> saveList = new ArrayList<>();

            if (currentType == 1) {
                //1. 按分类：批量设备 currentType等于1为按分类 则targetId为分类的ID 需要查询该分类下的所有有“最近一次保养时间”的所有设备
                LambdaQueryWrapperX<EquipmentManageDO> wrapper = new LambdaQueryWrapperX<>();
                wrapper.eq(EquipmentManageDO::getEquipmentClassify, targetId);
                wrapper.isNotNull(EquipmentManageDO::getLastMaintainTimeDamage);
                List<EquipmentManageDO> equipmentList = equipmentManageMapper.selectList(wrapper);

                if (equipmentList.isEmpty()) return;

                EquipmentKeepSettingTypeDO setting = equipmentKeepSettingTypeService.getEquipmentKeepSettingType(itemId);
                Integer cycle = setting.getCycle();

                //3.生成对应的待保养设备数据  为每个设备生成一个递增的单号
                String currentNo = nextBaseNo;
                for (EquipmentManageDO equipment : equipmentList) {
                    //判断有无已经生成了的 包括按设备的
                    LambdaQueryWrapper<EquipmnetKeepWaitDO> waitWrapper = new LambdaQueryWrapper<>();
                    waitWrapper.in(EquipmnetKeepWaitDO::getStatus, EXCLUDED_STATUS_SET);
                    waitWrapper.eq(EquipmnetKeepWaitDO::getEquipmentId, equipment.getId());
                    waitWrapper.orderByDesc(EquipmnetKeepWaitDO::getUpdateTime);

                    //如果存在的  排除掉 不应该插入 按分类的  应该查询按设备的保养是否存在
                    if (equipmnetKeepWaitMapper.exists(waitWrapper)) {
                        //查询是否有相同的两条以上的  只保留最新一条 删除掉旧数据
                        List<EquipmnetKeepWaitDO> moreInfo = equipmnetKeepWaitMapper.selectList(waitWrapper);
                        for (int i = 1; i < moreInfo.size(); i++) {
                            equipmnetKeepWaitMapper.deleteById(moreInfo.get(i).getId());
                        }
                    } else {
                        EquipmnetKeepWaitDO save = new EquipmnetKeepWaitDO();

                        //取设备里面的维护人员
                        save.setDutyUserId(equipment.getMaintenanceUserIdDamage());

                        save.setKeepNo(currentNo); // 第一个设备用 baseNo
                        save.setItemId(itemId);//保养项ID
                        save.setEquipmentId(equipment.getId());//设备ID
                        save.setStatus(KeepStatusEnum.WAIT.getKeepStatus());//状态 新增设置为待派工
                        save.setEquipmentName(equipment.getEquipmentName());
                        //最近一次保养时间

                        if (equipment.getLastMaintainTimeDamage() != null) {
                            LocalDate lastDate = equipment.getLastMaintainTimeDamage().toLocalDate();
                            save.setPlanDate(lastDate.plusDays(setting.getCycle()));
                        } else {
                            save.setPlanDate(LocalDate.now().plusDays(setting.getCycle()));
                        }

                        saveList.add(save);
                        // 生成下一个单号（BY202508001 → BY202508002）
                        currentNo = CreateKeepNo.generateNextCode(currentNo);
                    }

                }

            } else if (currentType == 2) {
                // 按设备：单个设备
                EquipmentManageDO equipment = equipmentManageMapper.selectById(targetId);
                if (equipment == null) {
                    return;
                }

                //判断有无已经生成了的
                LambdaQueryWrapper<EquipmnetKeepWaitDO> waitWrapper = new LambdaQueryWrapper<>();
                waitWrapper.in(EquipmnetKeepWaitDO::getStatus, EXCLUDED_STATUS_SET);
                waitWrapper.eq(EquipmnetKeepWaitDO::getEquipmentId, equipment.getId());

                //如果存在的  排除掉 不应该插入
                if (equipmnetKeepWaitMapper.exists(waitWrapper)) {
                    //查询是否有相同的两条以上的  只保留最新一条 删除掉旧数据
                    List<EquipmnetKeepWaitDO> moreInfo = equipmnetKeepWaitMapper.selectList(waitWrapper);
                    for (int i = 1; i < moreInfo.size(); i++) {
                        equipmnetKeepWaitMapper.deleteById(moreInfo.get(i).getId());
                    }
                } else {
                    EquipmnetKeepWaitDO save = new EquipmnetKeepWaitDO();

                    //取设备的维护人员
                    save.setDutyUserId(equipment.getMaintenanceUserIdDamage());

                    save.setKeepNo(nextBaseNo); // 直接使用生成的第一个号
                    save.setItemId(itemId);
                    save.setEquipmentId(equipment.getId());
                    save.setStatus(KeepStatusEnum.WAIT.getKeepStatus());
                    save.setEquipmentName(equipment.getEquipmentName());
                    EquipmentKeepSettingTypeDO setting = equipmentKeepSettingTypeService.getEquipmentKeepSettingType(itemId);
                    if (equipment.getLastMaintainTimeDamage() != null) {
                        LocalDate lastDate = equipment.getLastMaintainTimeDamage().toLocalDate();
                        save.setPlanDate(lastDate.plusDays(setting.getCycle()));
                    } else {
                        save.setPlanDate(LocalDate.now().plusDays(setting.getCycle()));
                    }


                    saveList.add(save);
                }
            }

            // 批量插入（事务内）
            if (!saveList.isEmpty()) {
                equipmnetKeepWaitMapper.insertBatch(saveList);
            }

        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new RuntimeException("生成单号时被中断", e);
        } catch (Exception e) {
            throw new RuntimeException("生成维护任务失败: " + e.getMessage(), e);
        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }
}
